#include"ProcessPool.h" 
const int processnum = 5; //五个子进程



//

vector<task_t> tasks;

class channel
{
public:
    channel(int cmdfd, pid_t slarverid, string processname)
        :_cmdfd(cmdfd)
        ,_slarverid(slarverid)
        ,_processname(processname)
    {}
    
public:
    int _cmdfd; //文件描述符: 表示父进程的连接某个管道的fd是多少
    pid_t _slarverid;   //表示管道的连接的子进程的PID
    string _processname; //管道连接的子进程的名字
};


void Slaver()
{
    int cmdcode = 0;
    while (true)
    {
        int n = read(0, &cmdcode, sizeof(cmdcode));    //read返回的是读取到的字节的个数。 
        //然后第二个参数是要读到哪里去， 第三个参数是读取的大小,这一行代码执行完后， cmdcode里面保存的就是要执行的任务码

        if (n == sizeof(cmdcode))
        {
            cout << "slaver get a command : " << getpid() << " : " << "cmdcode" << cmdcode << endl;
            if (cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }
        if (n == 0) break; //如果读到0， 说明写端关闭， 读端读到文件末尾， 就需要停止读取了
        
        cout << "Process PID: " << getpid() << "quit" << endl;
    }
}


//创建管道函数
void InitProcess(vector<channel>* channels)
{
    vector<int> oldfds; //创建一个数组， 用来保存父进程占用的文件描述符
    for (int i = 0; i < processnum; i++)
    {
        //创建管道
        int pipefd[2];
        int n = pipe(pipefd);  //创建一个管道， 并且父进程的读写端都指向这个管道
        if (n != 0) exit(0); //如果创建失败， 直接退出进程
        
        //创建子进程
        pid_t id = fork(); //创建子进程
        if (id == 0)       //执行子进程模块 
        {
            for (auto fd : oldfds) close(fd);   //遍历整个数组， 将已经占用的文件描述符关闭。
            close(pipefd[1]);    //子进程用来读， 所以要关闭子进程的写端
            //核心代码
            dup2(pipefd[0], 0);  //将0号描述符作为读端描述符，（其实这里不用dup2也可以， 博主也不知道这段代码段作用） 但是这么设计是为了更规范，这是一种设计方式
            Slaver(); //核心代码

            exit(0);   
        }
        close(pipefd[0]);
        string name = "process-" + to_string(i);
        channels->push_back(channel(pipefd[1], id, name));  

        oldfds.push_back(pipefd[1]); //记录当前父进程的pipefd[1]里面的占用的文件描述符
    }
}

//创建菜单
void menu()
{
    cout << "*****************************************" << endl;
    cout << "******  1.更新日志      2.更新野区   ******" << endl;
    cout << "******  3.检测更新      4.释放技能   ******" << endl;
    cout << "******  0.退出                      ******" << endl;
    cout << "*****************************************" << endl;
}

//控制子进程
void CtrlSlaver(vector<channel>& channels)
{
    int which = 0;
    int select = 0;

    while(true)
    {
        //1.选择任务， 选择进程
        menu();

        //输入选择，选择执行的任务
        cin >> select;
        if (select <= 0 ||  select >= tasks.size()) break;  //判断select是否合法, 不合法就退出
        int cmdcode = select;
        //
        //2.选择进程
        int processpos = which;  //选择进程使用轮询法， 就是使用which;
        
        cout << "father say cmdcode : " << cmdcode 
            << " already sendto " << channels[which]._cmdfd 
                << " processname : " << channels[which]._processname << endl;   //测试代码打印

        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode)); //第一个参数为fd输入性参数， 第二个参数为cmdcode要写入的首地址， 
        which++;
        which %= channels.size();

        sleep(1);
    }
}


//清理收尾

void QuitProcess(const vector<channel> &channels)
{
    for (const auto e : channels) 
    {
        close(e._cmdfd);  //写端
        waitpid(e._slarverid, nullptr, 0);
    }
}

int main()
{
    LoadTask(&tasks); //程序打开时就将tasks准备好
    

    //再组织的过程
    vector<channel> channels;


    //创建管道
    InitProcess(&channels);

    //控制子进程
    CtrlSlaver(channels);

    //清理收尾
    QuitProcess(channels);


    return 0;
}
